﻿
using System.Xml.Linq;

namespace Sidvall.Serialization
{
    public class Serializer : Sidvall.Serialization.ISerializer
    {
        #region Public Members

        #region Deserialize

        public object Deserialize(string value)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject(value);
        }
        public object Deserialize(string value, SerializerSettings settings)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject(value, GetSettings(settings));
        }

        public T Deserialize<T>(string value)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(value);
        }
        public T Deserialize<T>(string value, SerializerSettings settings)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(value, GetSettings(settings));
        }

        #endregion
        #region DeserializeXml

        public XDocument DeserializeXml(string value, string deserializeRootElementName, bool writeArrayAttribute)
        {
            var converter = new XmlNodeConverter()
            {
                DeserializeRootElementName = deserializeRootElementName,
                WriteArrayAttribute = writeArrayAttribute,
            };
            return (XDocument)Newtonsoft.Json.JsonConvert.DeserializeObject(value, typeof(XDocument), converter);
        }

        #endregion
        #region Serialize

        public string Serialize(object value)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(value);
        }
        public string Serialize(object value, SerializerSettings settings)
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(value, GetSettings(settings));
        }

        #endregion
        #region SerializeXml

        public string SerializeXml(XNode node, bool omitRootObject, SerializerSettings settings)
        {
            var converter = new XmlNodeConverter()
            {
                OmitRootObject = omitRootObject,
            };
            if (settings != null)
            {
                converter.PropertyItems.AddRange(settings.PropertyItems);
                return Newtonsoft.Json.JsonConvert.SerializeObject(node, GetFormatting(settings.Formatting), converter);
            }
            else
            {
                return Newtonsoft.Json.JsonConvert.SerializeObject(node, Newtonsoft.Json.Formatting.None, converter);
            }
        }

        #endregion
        #region TryDeserialize

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T TryDeserialize<T>(string value, System.Func<T> getDefaultValueHandler)
        {
            if (getDefaultValueHandler == null)
                throw new System.ArgumentNullException(nameof(getDefaultValueHandler));
            if (string.IsNullOrWhiteSpace(value))
                return getDefaultValueHandler();
            var trimmedValue = value.Trim();
            if ((!trimmedValue.StartsWith("[", System.StringComparison.OrdinalIgnoreCase)) && (!trimmedValue.StartsWith("{", System.StringComparison.OrdinalIgnoreCase)))
                return getDefaultValueHandler();
            try
            {
                return Deserialize<T>(value);
            }
            catch
            {
                return getDefaultValueHandler();
            }
        }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public T TryDeserialize<T>(string value, System.Func<T> getDefaultValueHandler, SerializerSettings settings)
        {
            if (getDefaultValueHandler == null)
                throw new System.ArgumentNullException(nameof(getDefaultValueHandler));
            if (string.IsNullOrWhiteSpace(value))
                return getDefaultValueHandler();
            var trimmedValue = value.Trim();
            if ((!trimmedValue.StartsWith("[", System.StringComparison.OrdinalIgnoreCase)) && (!trimmedValue.StartsWith("{", System.StringComparison.OrdinalIgnoreCase)))
                return getDefaultValueHandler();
            try
            {
                return Deserialize<T>(value, settings);
            }
            catch
            {
                return getDefaultValueHandler();
            }
        }

        #endregion

        #region ToString

        public override string ToString()
        {
            return "Serializer - Newtonsoft (PCL)";
        }

        #endregion

        #endregion
        #region Private Members

        #region GetFormatting

        private static Newtonsoft.Json.Formatting GetFormatting(Formatting formatting)
        {
            switch (formatting)
            {
                case Formatting.Indented:
                    return Newtonsoft.Json.Formatting.Indented;
                default:
                    return Newtonsoft.Json.Formatting.None;
            }
        }

        #endregion
        #region GetSettings

        private static Newtonsoft.Json.JsonSerializerSettings GetSettings(SerializerSettings settings)
        {
            var item = new Newtonsoft.Json.JsonSerializerSettings();
            if (settings != null)
            {
                item.Formatting = GetFormatting(settings.Formatting);
                switch (settings.ReferenceLoopHandlingPolicy)
                {
                    case ReferenceLoopHandlingPolicy.Error:
                        item.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Error;
                        break;
                    case ReferenceLoopHandlingPolicy.Serialize:
                        item.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
                        break;
                    default:
                        item.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                        break;
                }
                switch (settings.NullValueHandlingPolicy)
                {
                    case NullValueHandlingPolicy.Ignore:
                        item.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
                        break;
                    default:
                        item.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
                        break;
                }
                switch (settings.TypeNameHandling)
                {
                    case TypeNameHandlingPolicy.All:
                        item.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;
                        break;
                    case TypeNameHandlingPolicy.Objects:
                        item.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects;
                        break;
                    case TypeNameHandlingPolicy.Arrays:
                        item.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Arrays;
                        break;
                    case TypeNameHandlingPolicy.Auto:
                        item.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;
                        break;
                    default:
                        item.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
                        break;
                }
                item.Converters = new System.Collections.Generic.List<Newtonsoft.Json.JsonConverter>();
                switch (settings.EnumHandlingPolicy)
                {
                    case EnumHandlingPolicy.Text:
                        item.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()
                        {
                            CamelCaseText = false,
                        });
                        break;
                    case EnumHandlingPolicy.CamelCaseText:
                        item.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()
                        {
                            CamelCaseText = true,
                        });
                        break;
                    default:
                        break;
                }
                if ((settings.PropertyItems.Count > 0) || (settings.DefaultDisplayPositionPolicy != DisplayPositionPolicy.None))
                {
                    item.ContractResolver = new DynamicContractResolver(settings.PropertyItems, settings.DataTypePropertyItems,
                        settings.DefaultDisplayPositionPolicy, settings.DefaultDisplayPosition);
                }
            }
            return item;
        }

        #endregion

        #endregion
    }
}
